local M = {}
local windows = require'workstation.windows'

-- 构造可切换的缓冲区信息
function M.__get_buf_list()
    local buf_info_list = {}
    for _, bid in ipairs(vim.api.nvim_list_bufs()) do 
        -- 通过buffer的option查看buf 的性质
        -- 从 :help option-list 参考中查询得
        -- 从 :help api-buffer 参看buffer相关的api
        -- 从 :help api-window 参看window相关的api

        local buf_info = {}

        --  是否是可以出现在列表中
        local buf_listed = vim.api.nvim_buf_get_option(bid, 'buflisted')
        --  是否是隐藏的
        local hidden = vim.api.nvim_buf_get_option(bid, 'bufhidden')
        --  是否是loaded的
        local loaded = vim.api.nvim_buf_is_loaded(bid)
        --  是否是有修改的
        local modified = vim.api.nvim_buf_get_option(bid, 'modified')

        -- buf 是可以在列表中展示的
        if buf_listed and hidden ~= 'hidden' then
            local line_text = string.format("#%-5d", bid) .. ' '
            -- 当前活动文档
            if loaded then
                line_text = line_text .. '>'
            else
                line_text = line_text .. ' '
            end
            -- 当前文档有未保存的修改 
            if modified then
                line_text = line_text .. 'M'
            else
                line_text = line_text .. ' '
            end
            -- 添加文档路径
            local file_path = vim.api.nvim_buf_get_name(bid)
            line_text = line_text .. '  ' .. file_path

            -- 读取缓存的最后一次查看时间
            local status, last_access_time = pcall(function() 
                return vim.api.nvim_buf_get_var(bid, 'workstation-enter-time') 
            end)
            if not status then
                last_access_time = 0
            end 

            buf_info.loaded = loaded
            buf_info.modified = modified
            buf_info.file_path = file_path
            buf_info.text = line_text
            buf_info.last_access_time = last_access_time
            table.insert(buf_info_list, buf_info)
        end
    end
   
    -- 按照缓存的最后一次查看时间进行排存
    table.sort(buf_info_list, function(l, r)
        return l.last_access_time > r.last_access_time
    end)

    return buf_info_list
end

function M.__refresh(bufnr, win_id)
    -- 窗口的基本信息
    local win_width = vim.api.nvim_win_get_width(win_id)
    local win_height = vim.api.nvim_win_get_height(win_id)
    
    -- 开始填充缓冲内容
    local line_split = ''  
    for i = 1, win_width do
        line_split = line_split .. '-'
    end

    -- 构造文本
    local lines = {
        "Select Buffers: (move cursor onto file-row, and press ENTER)",
        line_split
    }

    local buf_infos = M.__get_buf_list()
    for _, buf_info in ipairs(buf_infos) do
        table.insert(lines, buf_info.text)
    end
    
    -- 添加文本
    vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, lines)

    -- 重新设置光标位置
    if table.maxn(buf_infos) > 0 then
        vim.api.nvim_win_set_cursor(win_id, { 3, 0 })
        M.__set_highlight_line(bufnr, 3)
    end
end

-- 移动当前的选择行
function M.move_cur_select_line(direct)
    local bufnr = vim.api.nvim_get_current_buf()
    local win_id = vim.api.nvim_get_current_win()
   
    local row_min = 3
    local row_max = vim.api.nvim_buf_line_count(bufnr)
    local cursor = vim.api.nvim_win_get_cursor(win_id)
    local org_cursor_line = cursor[2]
    local cursor_changed = false
    if direct == 'up' then 
        if cursor[1] - 1 >= row_min then
            cursor[1] = cursor[1] - 1
            cursor_changed = true
        end
    else 
        if cursor[1] + 1 <= row_max then
            cursor[1] = cursor[1] + 1
            cursor_changed = true
        end
    end
    
    if not cursor_changed then
        return
    end
    
    M.__clear_highlight_line(bufnr)
    vim.api.nvim_win_set_cursor(win_id, cursor)
    M.__set_highlight_line(bufnr, cursor[1])
end

function M.destroy_dialog() 
    M.__clear_highlight_line(0)
    vim.api.nvim_command('bd')
    M.__delete_namespace()
end

-- 返回高亮的名字空间
function M.__create_or_query_namespace() 
    local const_namespace_num_key = 'workstation.namespace.id'
    local err, ns_id = pcall(function() return vim.api.nvim_buf_get_var(0, const_namespace_num_key) end)
    if not err then
        ns_id = vim.api.nvim_create_namespace(const_namespace_num_key)
        vim.api.nvim_buf_set_var(0, const_namespace_num_key, ns_id)
    end
    assert(ns_id and ns_id > 0)
    return ns_id
end

-- 删除高亮名字空间
function M.__delete_namespace()
end

-- 高亮指定行
function M.__set_highlight_line(bufnr, line_num) 
    local ns_id = M.__create_or_query_namespace()
    -- 可用的高亮组名称，可以使用 :hi 来查看可用的选项
    vim.api.nvim_buf_add_highlight(bufnr, ns_id, 'WildMenu', line_num - 1, 1, -1)
end

-- 删除指定行高亮
function M.__clear_highlight_line(bufnr)
    local ns_id = M.__create_or_query_namespace()
    vim.api.nvim_buf_clear_highlight(bufnr, ns_id, 0, -1)
end

-- 切换到当前选择的文件
function M.enter_file()
    local select_line = vim.api.nvim_get_current_line()
    local bufnr = string.gsub(select_line, "#(%d+).*", "%1")
    local command = ":bd|buffer " .. bufnr .. ""
    vim.api.nvim_command(command)
end

-- 记录每个缓存区的最后一次访问时间，用来在缓存表中按照时间排序
function M.record_enter_time()
    local bufnr = vim.api.nvim_get_current_buf()
    local cur_time = os.time()
    vim.api.nvim_buf_set_var(bufnr, 'workstation-enter-time', cur_time)
end

-- 展示缓存区选择对话框
function M.show_buf_select_dialog() 
    -- 创建浮动窗口
    local win_info = windows.percentage_range_window(0.8, 0.6)
    local bufnr, win_id = win_info.bufnr, win_info.win_id

    -- 刷新窗口内容
    M.__refresh(bufnr, win_id)

    -- 禁用文档编辑
    vim.api.nvim_buf_set_option(bufnr, 'modifiable', false)

    -- 按ESC退出窗口
    vim.api.nvim_buf_set_keymap(bufnr, 'n', '<esc>', '<cmd>lua require "workstation.bufselect".destroy_dialog()<CR>', { noremap = true })
    -- 按 j 选择下一个文件
    vim.api.nvim_buf_set_keymap(bufnr, 'n', 'j', '<cmd>lua require "workstation.bufselect".move_cur_select_line("down")<CR>', { noremap = true })
    -- 按 k 选择上一个文件
    vim.api.nvim_buf_set_keymap(bufnr, 'n', 'k', '<cmd>lua require "workstation.bufselect".move_cur_select_line("up")<CR>', { noremap = true })
    -- 按 g 选择切换文件
    vim.api.nvim_buf_set_keymap(bufnr, 'n', '<CR>', '<cmd>lua require "workstation.bufselect".enter_file()<CR>', { noremap = true })
end

return M
